package com.example.aigc_education.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.aigc_education.common.BusinessException;
import com.example.aigc_education.domain.Course;
import com.example.aigc_education.domain.CourseWeekDate;
import com.example.aigc_education.domain.dto.CourseMongoDto;
import com.example.aigc_education.domain.vo.CourseVO;
import com.example.aigc_education.domain.vo.CourseWeekDateVO;
import com.example.aigc_education.repository.CourseRepository;
import com.example.aigc_education.repository.CourseWeekDateRepository;
import com.example.aigc_education.service.CourseService;
import com.example.aigc_education.mapper.CourseMapper;
import com.example.aigc_education.service.UserCourseService;
import com.example.aigc_education.utils.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 23918
 * @description 针对表【stu_course】的数据库操作Service实现
 * @createDate 2024-06-04 17:48:24
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
        implements CourseService {

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private CourseRepository courseRepository;

    @Resource
    private CourseWeekDateRepository weekDateRepository;

    @Resource
    private UserCourseService userCourseService;

    @Resource
    private MongoTemplate mongoTemplate;


    /**
     * 添加课程信息
     *
     * @param courseMongoDto 课程对象
     * @return 返回课程对象
     */
    @Override
    @Transactional
    public void addCourseInMongo(CourseMongoDto courseMongoDto){

        try {
            // 获取用户id
            Long userId = SecurityUtils.getUserId();
            //Long userId = 123456789L;

            // week_date的VO对象
            List<CourseWeekDateVO> weekDateVOS = new ArrayList<>();

            // 获取并且判断week_date是否为空
            // 为空————数据库已经有数据
            List<CourseWeekDate> weekDates = courseMongoDto.getWeekDate();
            Integer week = courseMongoDto.getCurrentWeek();

            // 不为空————数据库没有数据，手动添加到数据库
            if( !weekDates.isEmpty() ){
                // 查找数据库是否已经存在这一周的日期
                Query query = new Query(Criteria.where("date").is(weekDates.get(0).getDate()));
                CourseWeekDate courseWeekDate = mongoTemplate.findOne(query, CourseWeekDate.class);

                if( courseWeekDate == null ){
                    // 日期为空，添加
                    for ( CourseWeekDate weekDate : weekDates){

                        // 给每一个week_date的week赋值并且保存到数据库
                        weekDate.setWeek(week);
                        weekDate.setYear(courseMongoDto.getCourseList().get(0).getYear());
                        weekDate.setSemester(courseMongoDto.getCourseList().get(0).getSemester());

                        CourseWeekDateVO weekDateVO = new CourseWeekDateVO(weekDate.getWeekDay(), weekDate.getDate());
                        weekDateVOS.add(weekDateVO);

                        weekDateRepository.insert(weekDate);
                    }

                }

            }

            // 创建VO对象，处理字段信息
            CourseVO courseVO = new CourseVO("year",
                    courseMongoDto.getCourseList().get(0).getSemester(),
                    courseMongoDto.getCurrentWeek(),
                    courseMongoDto.getCourseList(),
                    weekDateVOS);
            processFields(courseVO, true );

            // 根据time来保存几份到数据库
            String[] times = courseVO.getCourseList().get(0).getTime().split(",");
            for ( String item : times ){
                // 对象拷贝————可以选择忽略source里面的元素，这里不需要
                Course newCourse = new Course();
                BeanUtils.copyProperties(courseVO.getCourseList().get(0), newCourse);

                newCourse.setTime(item);
                courseRepository.insert(newCourse);

                //保存信息到用户与课表的关联表
                userCourseService.addUserCourseId(userId, newCourse.getCourseId());

            }

/*
            // 保存数据到数据库
            Course newCourse = courseVO.getCourseList().get(0);
            courseRepository.insert(newCourse);
*/

/*            // 保存信息到用户与课表的关联表
            userCourseService.addUserCourseId(userId, newCourse.getCourseId());*/


        } catch (Exception e) {
            throw new BusinessException("添加课程失败：\n" + e.getMessage() );
        }

    }

    /**
     * 展示所有的课表信息
     *
     * @return 课表信息集合
     */
    @Override
    public CourseVO selectAllCourseInMongo(Integer year, Integer semester) {
        try{
            // 获取用户id
            Long userId = SecurityUtils.getUserId();
            //Long userId = 123456789L;

            // 根据用户id查询课程信息
            Iterable<String> courseIds = userCourseService.selectAllCourseIdByUserId(userId);

            // 封装的方法————根据周数升序的课程
            // 与用户关联的课表信息
            Iterable<Course> list = courseRepository.findAllById(courseIds);
            // iterable 转为list
            List<Course> courseList = new ArrayList<>();
            for (Course course : list){

                // 判断学年和学期是否一样
                if( course.getYear().equals(year)   &&   course.getSemester().equals(semester) ){
                    courseList.add(course);
                }

            }


            // 默认返回第一周的时间日期
            Query query = new Query(Criteria.where("week").is(1));
            List<CourseWeekDateVO> weekDate = mongoTemplate.find(query, CourseWeekDate.class).stream().map((item) ->{
                // 使用集合的stream流将week_date转换成week_dateVO
                CourseWeekDateVO weekDateVO = new CourseWeekDateVO();
                weekDateVO.setDate(item.getDate());
                weekDateVO.setWeekDay(item.getWeekDay());

                return weekDateVO;
            }).collect(Collectors.toList());


            // 默认current_week为第一周
            CourseVO courseVO = new CourseVO("year", semester, 1 ,courseList, weekDate);
            processFields(courseVO, false );

            // 返回排序之后的course
            return sortCourse(courseVO);

        } catch (Exception e) {
            throw new BusinessException("课表全部获取失败:\n" + e.getMessage() );
        }
    }


    /**
     *  更新课程信息
     *  因为mongo的save是id存在时候会修改，不存在就插入新数据，所以做一个判断
     * @param newCourse          更新后的课程信息
     * @return          返回更新后的课程信息
     */
/*    public Course updateCourseInMongo(Course newCourse) {
        try {
            // 根据课程id查找课程
            Query query = new Query(Criteria.where("_id").is(newCourse.getCourseId()));
            Course course = mongoTemplate.findOne(query, Course.class);

            // 判断课程是否存在
            if( course != null){

                List<Course> courses = new ArrayList<>();
                courses.add(newCourse);
                CourseVO courseVO = new CourseVO("year", course.getSemester(), 1 ,courses, null);

                // 当数据库有id时候，save就是更新
                course = courseRepository.save(processFields(courseVO, true ).getCourseList().get(0));

            }

            return course;

        } catch (DataAccessResourceFailureException exception){
            throw new BusinessException("查无此课程");

        } catch ( Exception e ){
            throw new BusinessException("修改失败");
        }
    }*/


    /**
     *  删除课表信息
     *  需要对course和user_course两个表进行修改
     * @param courseId      课表id
     */
/*    @Transactional
    public void deleteCourseByIdInMongo(String courseId) {
        try{

            // 删除course表的数据
            courseRepository.deleteById(courseId);

            // 删除与用户关联的信息
            userCourseService.deleteUserCourseByCourseId(courseId);

        } catch (Exception e){
            throw new BusinessException("删除失败");
        }

    }*/


    /**
     *  根据课程id搜索课程信息
     * @param courseId      课程id
     * @return              返回course对象
     */
/*    public Course selectCourseByIdInMongo(String courseId) {
        try{

            // 简化代码，直接返回，不用再创建一个Course对象
            return courseRepository.findById(courseId).get();

        } catch (Exception e){
            throw new BusinessException("课表获取失败");
        }

    }*/



    /**
     * 根据周数查找课程
     *
     * @param week      需要查询的周
     * @return          课程集合
     */
    @Override
    public CourseVO selectCourseByWeekInMongo(Integer year, Integer semester, Integer week) {
        try{

            // 获取course的VO对象
           CourseVO courseVO = getCourseVOS(year, semester, week );

            // 将课程信息排序并返回
            return courseVO;

        } catch (Exception e){
            throw new BusinessException("课表按周获取失败:" + e.getMessage() );
        }

    }


    /**
     *  根据日期查找日期所在的这一周的日期
     * @param date      日期
     * @return          courseVO对象
     */
    @Override
    public CourseVO selectCourseByDate( LocalDate date ) {

        // 根据date查找————获取week数据
        Query query1 = new Query(Criteria.where("date" ).is(date));
        CourseWeekDate courseWeekDate = mongoTemplate.findOne(query1, CourseWeekDate.class);

        if ( courseWeekDate != null ){
            // 获取学年和学期
            int year = courseWeekDate.getYear();
            int semester = courseWeekDate.getSemester();
            int week = courseWeekDate.getWeek();

            // 获取course的VO对象————并且处理字段
            return getCourseVOS(year, semester, week);

        }

        // 不存在就会返回null
        return null;
    }


    /**
     * 处理添加或者更新，展示时候的courseTime的数据处理
     *
     * @param courseVO 添加或者更新的courseVO对象
     * @param flag     确认是否需要处理第几节的问题，对于展示是不需要
     * @return 处理成功后的数据
     */
    private CourseVO processFields(CourseVO courseVO, Boolean flag){
        Course course = courseVO.getCourseList().get(0);

        if ( flag ){
            // 处理星期几
            Integer weekday = course.getWeekday();
            switch ( weekday ){
                case 1:
                    course.setCourseTime("星期一");
                    break;
                case 2:
                    course.setCourseTime("星期二");
                    break;
                case 3:
                    course.setCourseTime("星期三");
                    break;
                case 4:
                    course.setCourseTime("星期四");
                    break;
                case 5:
                    course.setCourseTime("星期五");
                    break;
                case 6:
                    course.setCourseTime("星期六");
                    break;
                case 7:
                    course.setCourseTime("星期日");
                    break;
                default:
                    throw new BusinessException("周几添加失败");
            }

            // 处理第几节
            String[] times = course.getTime().split(",");
            int length = times.length;
            StringBuilder time = new StringBuilder();

            if( length > 1 ){
                for ( int i = 0 ; i < length - 1; ++i) {
                    int lastNumber = Integer.parseInt( times[i].trim() );

                    // time的i+1存在，判断i+1与i是否是连续的，只做两节之间处理
                    if (  String.valueOf(lastNumber + 1).equals(times[i+1].trim()) ){
                        // 连接连续的数字
                        time.append(lastNumber).append("-").append(times[++i].trim());

                    }else{
                        // 不连续的数字
                        time.append(lastNumber);

                    }
                    time.append(",");

                }

                // 处理time的节数最后的节
                //Character lateNumber = time.charAt( time.length()-2 );
                String lateNumber = time.substring(time.length()-3 );
                if( !lateNumber.contains(times[length-1]) ){
                    time.append(times[length-1]);
                    time.append(",");
                }
                time.setCharAt( time.length() - 1, '节');

            }else{
                time.append(times[0].trim()).append("节");
            }

            // 整理格式为：星期几x-x节
            String courseTime = course.getCourseTime();
            course.setCourseTime(courseTime + time );
        }


        int year = courseVO.getCourseList().get(0).getYear();
        // 处理VO里的year
        if( courseVO.getSemester() == 1 ){
           // 第一学期
            int lateYear = year + 1;
            // 学年是今年和下一年
            String yearStr = year + "-" +  lateYear + "学年";
            courseVO.setYear(yearStr);

        }else {
            // 第二学期
            int earlyYear = year - 1;
            // 学年是今年和上一年
            String yearStr = earlyYear + "-" + year + "学年";
            courseVO.setYear(yearStr);
        }

        return courseVO;
    }

    /**
     *  封装的方法用于上课时间周数日期升序
     * @param courseVO    待排序的VO对象
     * @return            排序之后的VO对象
     *
     *  此方法是将查询到的课程信息，进行上课时间升序，周数升序，日期升序
     */
    private CourseVO sortCourse(CourseVO courseVO){
        try {

            //排序
            // 根据上课时间第几节升序
            courseVO.getCourseList().sort(Comparator.comparing(Course::getTime));
            // 根据上课的星期几升序
            courseVO.getCourseList().sort(Comparator.comparing(Course::getWeekday));
            // 根据schedule来升序
            courseVO.getCourseList().sort(Comparator.comparing(Course::getSchedule));


            // 返回排序好的结果集
            return courseVO;

        } catch (Exception e){
            throw new BusinessException("课表排序失败");
        }


    }



    /**
     *  根据学年，学期，周获取course的VO对象
     * @param year          学年
     * @param semester      学期
     * @param week          周数
     * @return              course的VO对象
     */
    private CourseVO getCourseVOS(Integer year, Integer semester, Integer week ){

        // 获取用户的符合条件的course集合
        CourseVO courseList = selectAllCourseInMongo(year, semester);

        List<Course> courses = new ArrayList<>();
        for ( Course item : courseList.getCourseList()){

            // 判断week是否处于schedule的范围里面
            if( isWeekRange(item.getSchedule(), week )) {
                // 在范围里
                courses.add(item);
            }

        }



        // 查找week_date数据
        Criteria criteria = new Criteria();
        criteria.andOperator(Criteria.where("year").is(year),
                Criteria.where("semester").is(semester),
                Criteria.where("week").is(week));

        Query query = new Query(criteria);

        // 返回week_date的VO对象
        List<CourseWeekDateVO> weekDates = mongoTemplate.find(query, CourseWeekDate.class).stream().map((item) -> {
            // 将week_date 换成 VO对象输出
            CourseWeekDateVO weekDateVO = new CourseWeekDateVO();
            weekDateVO.setWeekDay(item.getWeekDay());
            weekDateVO.setDate(item.getDate());

            return weekDateVO;
        }).collect(Collectors.toList());


        // 将course信息换成VO对象输出
        CourseVO courseVOS = new CourseVO();
        courseVOS.setYear("year");
        courseVOS.setCurrentWeek(week);
        courseVOS.setCourseList(courses);
        courseVOS.setWeekDate(weekDates);
        // 没有课程的时候
        if ( !courses.isEmpty() ){
            courseVOS.setSemester(courses.get(0).getSemester());

        }else{
            return courseVOS;
        }


        // 处理字段并且排序
        return sortCourse(processFields(courseVOS, false ));

    }

    /**
     *  判断周是否在范围里面
     * @param schedule  上课的周数
     * @param week      想要查询的所在周
     * @return          布尔值
     */
    private boolean isWeekRange(String schedule, Integer week){

        try {

            // 根据据逗号分割字符串
            String[] ranges = schedule.split(",");

            for ( String range : ranges ){
                // 移除“周”、“周（单）、周（双）”

                if( range.contains("周(单)")  ){
                    // 处理单周
                    if( week % 2 != 0 ){
                        if( weekRange(range.replace("周(单)", ""), week) )
                            return true;
                    }else
                        return false;
                }

                else if (range.contains("周(双)") ){
                    // 处理双周
                    if( week % 2 == 0 ){
                         if ( weekRange(range.replace("周(双)", "" ), week) )
                             return true;
                    }else
                        return false;

                }else {
                    // 非单双周
                    if ( weekRange(range.replace("周", ""), week) )
                        return true;
                }
            }

        } catch (Exception e ){

            throw new BusinessException("课程范围区间失败");
        }

        return false;
    }

    /**
     * 抽取重复代码出来处理范围
     * @param range     范围
     * @return          布尔值
     */
    private boolean weekRange(String range, Integer weekNumber ){

        try{

            if( range.contains("-")) {
                // 处理范围，例如 1-9
                String[] bounds = range.split("-");

                // 使用trim，确保数字两边没有空白字符
                int lowerBound = Integer.parseInt(bounds[0].trim());
                int upperBound = Integer.parseInt(bounds[1].trim());

                return (weekNumber >= lowerBound && weekNumber <= upperBound);
            }else{
                int singleWeek = Integer.parseInt(range.trim());

                return (weekNumber == singleWeek);
            }

        } catch ( Exception e ){
            throw new BusinessException("课程范围修改失败");
        }

    }


    /**
     *  根据课程名字进行模糊查询
     * @param courseName    课程名字
     * @return              课程集合
     */
/*    public List<Course> selectCourseByNameInMongo(String courseName) {
        try{

            // 课程名字模糊查询
            Query query = new Query(Criteria.where("course_name").regex(courseName));
            List<Course> courseList = mongoTemplate.find(query, Course.class);

            // 获取用户id
            Long userId = SecurityUtils.getUserId();
            // Long userId = 123456789L;


            List<Course> courses = new ArrayList<>();
            for ( Course item : courseList){

                // 用户id和课程id匹配
                selectIsUserCourseId(userId, courses, item);
            }

            // 将找到的课程进行排序并返回
            return sortCourse(courses);

        } catch (Exception e){
            throw new BusinessException("课表获取失败");
        }

    }*/

    /**
     * 封装方法————用来查询用户与课表的关联信息
     * 搭配根据课程名字模糊查询使用
     *
     * @param userId  用户id
     * @param courses course集合
     * @param item    关联的course
     */
/*    private void selectIsUserCourseId(Long userId, List<Course> courses, Course item) {
        try{

            // 条件查询器
            // 根据用户id和课程id查询
            LambdaQueryWrapper<UserCourse> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserCourse::getCourseId, item.getCourseId());
            queryWrapper.eq(UserCourse::getUserId, userId);
            UserCourse uc = userCourseService.getOne(queryWrapper);

            // 课程不为空，添加到集合
            if ( uc != null ){
                courses.add(item);
            }

        } catch (Exception e){
            throw new BusinessException("课表获取失败");
        }
    }*/


    /**
     * 添加课程信息。
     *
     * 通过调用mongoTemplate的insert方法，尝试将一个新的课程对象插入到数据库中。
     * 如果插入过程中出现异常，将捕获该异常并抛出一个运行时异常，指示课程添加失败。
     * 这个方法的存在是为了允许系统管理员或有相应权限的用户添加新的课程到系统中。
     *
     * @param course 要添加的课程对象，包含课程的详细信息。
     * @throws BusinessException 如果课程添加过程中发生异常，则抛出此运行时异常。
     */
    @Override
    @Transactional
    public void addCourse(Course course) {
        try {
            // 调用CourseMapper的insert方法，将course对象插入数据库
             courseMapper.insert(course);

        } catch (Exception e) {
            throw new BusinessException("添加课程失败");
        }
    }


    /**
     * 更新课程信息。
     *
     * 通过传入的课程对象和课程ID，更新数据库中对应课程的信息。
     * 此方法使用了Try-Catch结构来捕获并处理可能发生的异常，确保在更新过程中出现异常时能够及时抛出运行时异常，
     * 提供给调用者明确的错误信息，提示更新课程操作失败。
     *
     * @param course 需要更新的课程对象，包含新的课程信息。
     * @param courseId 需要更新的课程的ID，用于定位具体课程记录。
     */
    @Override
    @Transactional
    public void updateCourse(Course course, Long courseId) {
        try {
            // 创建UpdateWrapper对象，用于构建更新条件。
            UpdateWrapper<Course> wrapper = new UpdateWrapper<>();
            // 设置更新条件为课程ID等于传入的courseId。
            wrapper.eq("course_id", courseId);
            // 调用CourseMapper的update方法，传入更新对象和更新条件，执行数据库更新操作。
            // 调用CourseMapper的update方法，更新course对象
            courseMapper.update(course, wrapper);
        } catch (Exception e) {
            // 捕获更新过程中可能出现的任何异常，抛出运行时异常并附带错误信息。
            throw new BusinessException("更新课程失败");
        }
    }

    /**
     * 删除课程信息。
     *
     * 本方法通过调用CourseMapper的deleteById方法，尝试删除指定ID的课程记录。
     * 如果删除过程中发生异常，方法将捕获该异常并抛出一个运行时异常，
     * 异常信息提示“删除课程失败”，以便上层调用者知道操作未成功。
     *
     * @param courseId 需要删除的课程的ID。
     */
    @Override
    @Transactional
    public void deleteCourse(Long courseId) {
        try {
            // 调用CourseMapper的deleteById方法，传入课程ID，删除对应的课程记录。
            courseMapper.deleteById(courseId);
        } catch (Exception e) {
            // 捕获删除过程中可能出现的任何异常，抛出运行时异常并附带错误信息。
            throw new BusinessException("删除课程失败");
        }
    }




}




